package com.ms.service.module.account.accountloginblocker;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import redis.clients.jedis.Jedis;

import com.google.gson.Gson;
import com.ms.service.db.jedis.JedisManager;
import com.ms.service.db.jedis.RedisResourceName;
import com.ms.service.error.ErrorCode;
import com.ms.service.module.account.accountloginblocker.db.AccountLoginIPBlockLogModel;
import com.ms.service.module.commonsettings.CommonAccountLoginBlockIPSetting;
import com.xpec.c4.db.basic.BasicLogDAO;
import com.xpec.c4.game.time.TimeSys;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.service.module.BasicServiceModule;

public class AccountLoginIPBlocker {
	private static final Log log = LogFactory.getLog(AccountLoginIPBlocker.class);
	
	private static String LOGIN_DISABLE_KEY = "IP:LOGIN_DISABLE:%s";
	
	private static String LOGIN_COUNT_KEY = "IP:LOGIN_COUNT:%s";
	
	public static int addAndCheck(BasicServiceModule module ,String ip, String accountId ,CommonAccountLoginBlockIPSetting blockIPSetting){
		Jedis jedis = null;
		try {
			jedis = getJedis();
			if (jedis == null) {
				if (log.isErrorEnabled()) {
					log.error(GameUtility.getCurMethodName() + "jedis is null");
				}
				return ErrorCode.DB_OPERATION_FAIL;
			}

			if (!blockIPSetting.isOpen()) {
				return ErrorCode.SUCCESS;
			}

			if (checkDisable(jedis, ip)) {
				return ErrorCode.ACCOUNT_IP_IS_BLOCKED;
			}

			// 加到列表
			long listLen = addLoginTime(jedis, ip, blockIPSetting.getDuration());

			// 如果當下超過設定次數
			if (listLen > blockIPSetting.getNumOfTry()) {
				// 要寫log 跟 disable data
				ArrayList<String> loginList = getLoginList(jedis, ip);
				long lockTime = TimeSys.currentTimeSec();
				long unLockTime = TimeSys.currentTimeSec() + blockIPSetting.getUnLockTime();
				AccountLoginIPDisableData data = new AccountLoginIPDisableData(ip, accountId, loginList, lockTime, unLockTime);

				// 寫到Redis
				addToDisableData(ip, blockIPSetting.getUnLockTime(), data);

				// 寫log
				AccountLoginIPBlockLogModel model = data.tranToModel(0);
				module.saveDbLog(model);
				
				return ErrorCode.ACCOUNT_IP_IS_BLOCKED;
			}

			return ErrorCode.SUCCESS;
		}
		catch (Exception e) {
			if (log.isErrorEnabled()) {
				log.error(e);
			}
			return ErrorCode.DB_OPERATION_FAIL;
		}
		finally {
			if (jedis != null) {
				returnJedis(jedis);
			}
		}
	}
	
	/**
	 * 移除被擋住的帳號 (ip)
	 * @param ip
	 */
	public static void removeDisableIP(String ip) {
		Jedis jedis = getJedis();
		removeDisableIP(jedis,ip);
		returnJedis(jedis);
	}
	
	/**
	 * 移除被擋住的帳號 (時間範圍)
	 * @param start 開始
	 * @param end   結束
	 */
	public static void removeDisableIP(long start, long end) {
		Jedis jedis = getJedis();
		// 先撈出所有disable的
		String disableKeys = String.format(LOGIN_DISABLE_KEY, "*");
		Set<String> keys = jedis.keys(disableKeys);

		Gson gson = new Gson();
		// 取出來移除
		for (String key : keys) {
			String value = jedis.get(key);
			AccountLoginIPDisableData data = gson.fromJson(value, AccountLoginIPDisableData.class);
			long lockTime = data.getLockTime();
			if (start <= lockTime && lockTime <= end) {
				// 這段時間內，要清除
				String ip = data.getIp();
				removeDisableIP(jedis, ip);
			}
		}
		returnJedis(jedis);
	}
	
	private  static void removeDisableIP( Jedis jedis ,String ip){
		String disableKey = String.format(LOGIN_DISABLE_KEY, ip);
		String disableValue = jedis.get(disableKey);
		// 取看看這個key 有沒有資料
		if (GameUtility.isHaveString(disableValue)) {
			AccountLoginIPDisableData data = new Gson().fromJson(disableValue, AccountLoginIPDisableData.class);
			AccountLoginIPBlockLogModel model = data.tranToModel(1);
			BasicLogDAO.saveLog(model);
			jedis.del(disableKey);
		}

		String loginCountKey = String.format(LOGIN_COUNT_KEY, ip);
		Boolean isLoginCountExist = jedis.exists(loginCountKey);
		if (isLoginCountExist) {
			// 存在就移除
			jedis.del(loginCountKey);
		}
	}
	
	
	/**
	 * 查看是否被禁止
	 * @param ip
	 * @return
	 */
	private static boolean checkDisable(Jedis jedis, String ip){
		String key = String.format(LOGIN_DISABLE_KEY, ip);
		boolean result = jedis.exists(key);
		return result;
	}

	
	/**
	 * 增加登入時間
	 * @param ip
	 * @param expireTime
	 */
	private static long addLoginTime(Jedis jedis, String ip, int expireTime) {
		String key = String.format(LOGIN_COUNT_KEY, ip);
		long newLen = jedis.rpush(key, String.valueOf(TimeSys.currentTimeSec()));
		if(newLen == 1){
			//第一次才要加過期時間
			jedis.expire(key, expireTime);
		}		
		return newLen;
	}
	
	private static ArrayList<String> getLoginList(Jedis jedis, String ip){
		String key = String.format(LOGIN_COUNT_KEY, ip);
		List<String> list = jedis.lrange(key, 0, -1);
		return new ArrayList<String>(list);
	}
	
	/**
	 * 
	 * @param ip
	 * @param expireTime
	 * @param disableData
	 */
	public static void addToDisableData(String ip, int expireTime, AccountLoginIPDisableData disableData) {
		Jedis jedis = getJedis();
		String key = String.format(LOGIN_DISABLE_KEY, ip);
		String value = new Gson().toJson(disableData);
		long result = jedis.setnx(key, value);
		if(result == 1){
			jedis.expire(key, expireTime);
		}
		
		returnJedis(jedis);
	}
	
	private static Jedis getJedis(){
		return JedisManager.getResource(RedisResourceName.BLOCKER);
	}
	
	private static void returnJedis(Jedis jedis){
		JedisManager.returnResource(RedisResourceName.BLOCKER, jedis);
	} 
	
	
	
}
